import 'package:family_expense/features/bank_card/models/bank_card.dart';
import 'package:family_expense/features/bank_card/widgets/bank_shared.dart';
import 'package:family_expense/features/income_expense/constants/payment_type.dart';
import 'package:flutter/material.dart';
import 'package:my_flutter_core/my_flutter_core.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

class BankCardShared {
  static Widget buildBankCardName(BankCard? bankCard) {
    return Text(
      bankCard?.name ?? '',
      style: bankCard?.enabled == false
          ? const TextStyle(color: Colors.grey, fontStyle: FontStyle.italic)
          : null,
    );
  }

  /// Gets the related bank cards from all cards based on payment type.
  static List<BankCard> getRelatedBankCards(
    List<BankCard> allCards,
    String? paymentType,
  ) {
    List<BankCard> relatedCards;

    if (allCards.isNotEmpty) {
      if (paymentType == PaymentType.creditCard) {
        relatedCards = resortCreditCardList(
          allCards.where((p) => p.isCreditCard!).toList(),
        );
      } else {
        relatedCards = allCards.where((p) => !p.isCreditCard!).toList();
      }
    } else {
      relatedCards = [];
    }

    return relatedCards;
  }

  /// When I have two credit cards I like to use one over another based on its statement date.
  /// For example I have CCB card with statement date is 5th and CMB card with statement date is 15th.
  /// When current date is between 5th and 15th I prefer CCB. When current date is between 15th and next month 5th I prefer CMB.
  /// That would allow me to have maximal bill range for each credit card.
  static List<BankCard> resortCreditCardList(List<BankCard> bankCards) {
    // Clones it avoid compromising the input data.
    final clonedCards = bankCards.map((card) => card.clone()).toList();

    if (clonedCards.isEmpty) {
      return [];
    }

    final sortedCards = <BankCard>[];

    while (clonedCards.isNotEmpty) {
      final topCard = _getTopCard(clonedCards);
      sortedCards.add(topCard!);
      clonedCards.removeWhere((p) => p.id == topCard.id);
    }

    return sortedCards;
  }

  /// Shows a pick modal for bank card selection.
  /// [bankCards] is the list of all banks. The [ListTile] is generated based on it.
  /// [bankCard] is current bankCard for making [ListTile] selected.
  static void showBankCardModalBottomSheet(
    BuildContext context,
    List<BankCard?> bankCards,
    ValueChanged<BankCard> onTap, {
    String? title,
    BankCard? bankCard,
  }) {
    var initialScrollIndex = bankCards.indexWhere(
      (element) => element!.id == bankCard?.id,
    );

    if (initialScrollIndex < 0) {
      initialScrollIndex = 0;
    }

    // If only 2 bank cards exist then we don't have to scroll.
    final scollableList = ScrollablePositionedList.builder(
      itemCount: bankCards.length,
      initialScrollIndex: bankCards.length > 2 ? initialScrollIndex : 0,
      itemBuilder: (context, index) {
        final p = bankCards[index]!;

        return ListTile(
          leading: BankLogo(p.bank!),
          title: buildBankCardName(p),
          subtitle: p.isCreditCard == true
              ? Text(
                  '账单日${p.statementDate}号',
                  style: const TextStyle(fontSize: 12.0),
                )
              : null,
          selected: p.id == bankCard?.id,
          onTap: () => onTap(p),
        );
      },
    );

    showModalBottomSheet<void>(
      context: context,
      builder: (context) {
        if (title.isNullOrEmpty) {
          return scollableList;
        } else {
          return SizedBox(
            height: 220.0,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const SizedBox(height: 15.0),
                Text(title!, style: const TextStyle(fontSize: 18.0)),
                const SizedBox(height: 10.0),
                Expanded(child: scollableList),
              ],
            ),
          );
        }
      },
    );
  }

  static BankCard? _getTopCard(List<BankCard> bankCards) {
    // Clones it avoid compromising the input data.
    final clonedCards = bankCards.map((card) => card.clone()).toList();

    if (clonedCards.isEmpty) {
      return null;
    }

    // Sorts the credit cards based on its statement date in asc order.
    clonedCards.sort(
      (a, b) =>
          int.parse(a.statementDate!).compareTo(int.parse(b.statementDate!)),
    );

    final currentDay = getCurrentDateTime().day;
    BankCard? topCard;

    // Why we are picking up the first card due to the following reason.
    // Example:
    // - CCB Card, statement date is 5th. That means the bill covers last month 5th to this month 4th.
    // - CMB Card, statement date is 15th. That means the bill covers last month 15th to this month 14th.
    // If current day >= 5th && current day < 15th then the CMB card will be the first one.
    // If current day >= 15th && < 5th then the CCB card will be the first one.
    // That would allow me to have maximal bill range for each credit card.
    if (currentDay < int.parse(clonedCards.first.statementDate!) ||
        currentDay >= int.parse(clonedCards.last.statementDate!)) {
      // If current day is out of the range of the statement dates in the list we would take the last credit card as the first one in the return result.
      topCard = clonedCards.last;
    } else {
      // Checks each credit card's statement date but does not include the last one.
      for (var i = 0; i < clonedCards.length - 1; i++) {
        if (currentDay >= int.parse(clonedCards[i].statementDate!) &&
            currentDay < int.parse(clonedCards[i + 1].statementDate!)) {
          topCard = clonedCards[i];
          break;
        }
      }
    }

    return topCard;
  }
}
